//	Copyright (c) 2012 Jonathan Loe
//
//	MIT license (http://en.wikipedia.org/wiki/MIT_License)
//
//	Permission is hereby granted, free of charge, to any person obtaining a copy
//	of this software and associated documentation files (the "Software"), to deal
//	in the Software without restriction, including without limitation the rights 
//	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
//	of the Software, and to permit persons to whom the Software is furnished to do so, 
//	subject to the following conditions:
//
//	The above copyright notice and this permission notice shall be included in all 
//	copies or substantial portions of the Software.
//
//	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
//	INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
//	PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
//	FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
//	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Data.Linq;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;

namespace Ordinaire.Db
{
	/// <summary>
	/// This class handles the creation of database related classes.
	/// </summary>
    public class DbFactory
    {
        #region Variables

        /// <summary>
        /// Singleton instance.
        /// </summary>
        private static DbFactory instance;

        /// <summary>
        /// Connection string name.
        /// </summary>
        protected string defaultName = "default";

        #endregion

        #region Constructor

        /// <summary>
        /// Use Instance to get singleton instance.
        /// </summary>
        private DbFactory() { }

        #endregion

        #region Properties

        /// <summary>
        /// Gets DbFactory singleton.
        /// </summary>
        public static DbFactory Instance
        {
            get
            {
                if (instance == null)
                {
                    instance = new DbFactory();
                }

                return instance;
            }
        }

        /// <summary>
        /// Gets or sets default connection string name.
        /// </summary>
        public string DefaultName
        {
            get { return this.defaultName; }
            set { this.defaultName = value; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Returns default DataContext.
        /// </summary>
        /// <returns>data context</returns>
        public DataContext CreateDataContext()
        {
            return CreateDataContext<DataContext>();
        }

        /// <summary>
        /// Returns default DataContext of specified type.
        /// </summary>
        /// <typeparam name="T">derived type of DataContext</typeparam>
        /// <returns>data context of specified type T</returns>
        public T CreateDataContext<T>() where T : DataContext
        {
            this.defaultName.ThrowIfNullOrEmpty<InvalidOperationException>(ExceptionMessage.NoDefaultConnectionName);
            return CreateDataContext<T>(this.defaultName);
        }

        /// <summary>
        /// Returns default DataContext of specified type.
        /// </summary>
        /// <param name="type"></param>
        /// <returns>data context of specified type</returns>
        public DataContext CreateDataContext(Type type)
        {
            if (!type.IsSubclassOf(typeof(DataContext)))
            {
                throw ExceptionFactory.Create<NotSupportedException>(ExceptionMessage.TypeNotSupported, type.FullName);
            }
            this.defaultName.ThrowIfNullOrEmpty<InvalidOperationException>(ExceptionMessage.NoDefaultConnectionName);

            return CreateDataContext(type, this.defaultName);
        }

        /// <summary>
        /// Returns DataContext of specified connection string name.
        /// </summary>
        /// <param name="name">connection string name</param>
        /// <returns>data context of specified connection string name</returns>
        public DataContext CreateDataContext(string name)
        {
            return CreateDataContext<DataContext>(name);
        }

        /// <summary>
        /// Returns specified type T of specified connection string name from application config.
        /// </summary>
        /// <typeparam name="T">derived type of DataContext</typeparam>
        /// <param name="name">connection string name</param>
        /// <returns>instance of specified derived type of DataContext</returns>
        public T CreateDataContext<T>(string name) where T : DataContext
        {
            return (T) (object) CreateDataContext(typeof(T), name);
        }

        /// <summary>
        /// Returns DataContext of specified type and connection name.
        /// </summary>
        /// <param name="type">the DataContext type</param>
        /// <param name="name">the connection name</param>
        /// <returns>data context of specified type with specified connection name</returns>
        public DataContext CreateDataContext(Type type, string name)
        {
            if (type != typeof(DataContext) && !type.IsSubclassOf(typeof(DataContext)))
            {
                throw ExceptionFactory.Create<NotSupportedException>(ExceptionMessage.TypeNotSupported, type.FullName);
            }
            name.ThrowIfNullOrEmpty<ArgumentException>(String.Format(ExceptionMessage.IsNullOrEmpty, "name"));

            ConnectionStringSettings settings = ConfigurationManager.ConnectionStrings[name];
            DataContext instance = (DataContext) type.Instance(settings.ConnectionString);

            return instance;
        }

        #endregion
    }
}
